﻿#region Header

// Majesty of Omega - The next evolution of macro management space strategy
// 
// Copyright 2007-2009 André Claaßen
// 
// This file is protected by the terms and conditions of the
// Microsoft Community License (Ms-CL 1.1), a copy of which should
// have been distributed along with this software. If not,
// you may find the license information at the following URL:
// 
// http://www.microsoft.com/resources/sharedsource/licensingbasics/communitylicense.mspx

#endregion

#region

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;
using DesignByContract;
using System.Diagnostics;

#endregion

namespace Majesty_of_Omega.Config
{
    /// <summary>
    /// Enhancement of the Techtables class
    /// </summary>
    /// <remarks/>
    [XmlRoot(Namespace = "http://master-of-omega/TechTables.xsd", IsNullable = false)]
    public class TechTables : Spreadsheet
    {
        /// <summary>
        /// Dictionary for tech fields. The key is the 5-Digit Type-ID. 
        /// </summary>
        private Dictionary<string, TechField> techFieldDictionary;

        /// <summary>
        /// Dictionary for tech schools sorted by school. The key is the 2-Digit Type-ID
        /// </summary>
        private Dictionary<string, TechSchool> techSchoolDictionary;

        /// <summary>
        /// Dictionary of all tech applications
        /// </summary>
        private Dictionary<string, TechApplication> techAppDictionary;

        /// <summary>
        /// Access to a tech school with the type string
        /// </summary>
        /// <value>The tech school dictionary.</value>
        [XmlIgnore]
        public Dictionary<string, TechSchool> TechSchoolDictionary
        {
            get { return techSchoolDictionary; }
        }

        /// <summary>
        /// Gets the tech field dictionary. The key of the dictionary is the type_id
        /// </summary>
        /// <value>The tech field dictionary.</value>
        [XmlIgnore]
        public Dictionary<string, TechField> TechFieldDictionary
        {
            get { return techFieldDictionary; }
        }

        /// <summary>
        /// Gets the tech app dictionary. The key of this dictionary is the type_id of the 
        /// technology
        /// </summary>
        /// <value>The tech app dictionary.</value>
        [XmlIgnore]
        public Dictionary<string, TechApplication> TechAppDictionary
        {
            get { return techAppDictionary; }
        }

        /// <summary>
        /// Cached maximal technology level. This value will be calculated once.
        /// </summary>
        private int maxTechLevel;

        /// <summary>
        /// Gets the highest possible tech level.
        /// </summary>
        /// <value>The max tech level.</value>
        public int MaxTechLevel
        {
            get
            {
                if (maxTechLevel != 0) return maxTechLevel;

                maxTechLevel =  TechApplications.Max(techapp => techapp.DefaultTechFieldDependency.TechLevel);
                return maxTechLevel;
            }
        }

        /// <remarks/>
        [XmlArrayItem("TechSchool", IsNullable = false)]
        public TechSchool[] TechSchools { get; set; }

        /// <remarks/>
        [XmlArrayItem("TechField", IsNullable = false)]
        public TechField[] TechFields { get; set; }

        /// <remarks/>
        [XmlArrayItem("TechApp", IsNullable = false)]
        public TechApplication[] TechApplications { get; set; }

        /// <summary>
        /// This function will be called, to finish the construct process after read from xml
        /// </summary>
        public override void OnAfterReadFromXml()
        {
            try
            {
                MapTechFieldsToTechSchools();
                MapTechApplicationsToTechFields();
                techAppDictionary = TechApplications.ToDictionary(tech => tech.Type_ID);
                ResolveTechFieldDependencies();
            }
            catch (SpreadsheetException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new SpreadsheetException(
                    SpreadSheetType.TechTables,
                    "Exception occured while processing the TechTable.xml spreadsheet file.",
                    e);
            }
        }

        /// <summary>
        /// Resolves the tech field dependencies.
        /// </summary>
        private void ResolveTechFieldDependencies()
        {
            foreach (var techField in TechFields)
            {
                ResolveFieldDependencies(techField.Field_Dependencies,
                                         dependency => techField.TechFieldDependencies.Add(dependency));
            }
        }


        /// <summary>
        /// Maps every tech application to a technoly field.
        /// </summary>
        private void MapTechApplicationsToTechFields()
        {
            techFieldDictionary = TechFields.ToDictionary(techfield => techfield.Type_ID);

            foreach (var techApplication in TechApplications)
            {
                try
                {
                    ResolveFieldDependencies(techApplication.Field_Dependencies,
                     dependency =>
                     {
                         // The first Tech Field dependency is the major tech field dependency
                         if (techApplication.TechFieldDependencies.Count == 0)
                         {
                             dependency.TechField.TechApplications.Add(techApplication);
                         }
                         techApplication.TechFieldDependencies.Add(dependency);
                     });

                }
                catch (Exception)
                {

                    Debug.Print("Couldnot resolve " + techApplication.Field_Dependencies + " on " + techApplication.Name);
                }
            }
        }

        /// <summary>
        /// Resolves the field dependencies. You must specify a lambda action, to 
        /// handle every field dependency
        /// </summary>
        /// <param name="fieldDependencies">The field dependencies.</param>
        /// <param name="action">The action.</param>
        public void ResolveFieldDependencies(string fieldDependencies, Action<TechFieldDependency> action)
        {
            // Every Fielddependency string has the following syntax: Const_07
            // 5 Character Key to the Techfied and a 2 digit technology level number
            string[] fieldDependendiesStringList = fieldDependencies.Split(',');

            foreach (string fieldDep in fieldDependendiesStringList)
            {
                string[] tupel = fieldDep.Split('_');
                try
                {
                    var fieldDependency = new TechFieldDependency(GetTechFieldFromTypeID(tupel[0]),
                                                                  Convert.ToInt32(tupel[1]));
                    action(fieldDependency);
                }
                catch
                {

                }
                //catch (Exception e)
                //{
                //    //Debug.Assert(false);
                //    // Todo: Convert the second technology fields
                //    //throw new SpreadsheetException(SpreadSheetType.TechTables,
                //    //                               "Could not process Field-Dependency " + fieldDep +
                //    //                               " for tech application " + techApplication.Name, e);
                //}
            }
        }

        /// <summary>
        /// Gets the TechField from the type ID.
        /// </summary>
        /// <param name="techFieldTypeID">The tech field type ID.</param>
        /// <returns></returns>
        private TechField GetTechFieldFromTypeID(string techFieldTypeID)
        {
            return techFieldDictionary[techFieldTypeID.Trim()];
        }

        /// <summary>
        /// Maps the technology fields to technology schools.
        /// </summary>
        private void MapTechFieldsToTechSchools()
        {
            techSchoolDictionary = TechSchools.ToDictionary(techschool => techschool.Type_ID);

            foreach (TechField techField in TechFields)
            {
                try
                {
                    // make a two way binding between Techschool and Tech fields
                    techField.TechSchool = techSchoolDictionary[techField.School_Catagories];
                    techField.TechSchool.techFields.Add(techField);
                }
                catch (Exception e)
                {
                    throw new SpreadsheetException(SpreadSheetType.TechTables,
                                                   "Could not process Tech-School field for tech field" + techField.Name,
                                                   e);
                }
            }
        }
    }

    /// <summary>
    /// Enum of the 6 Technology Schools
    /// </summary>
    public enum TechSchoolType
    {
        /// <summary>
        /// Undefined Technology School
        /// </summary>
        TechSchoolUndefined,
        /// <summary>
        /// Tech school of Economy
        /// </summary>
        TechSchoolEconomy,
        /// <summary>
        /// Tech school of Energy
        /// </summary>
        TechSchoolEnergy,
        /// <summary>
        /// Tech school of Biology
        /// </summary>
        TechSchoolBiology,
        /// <summary>
        /// Tech school of Mathematics
        /// </summary>
        TechSchoolMathematics,
        /// <summary>
        /// Tech school of Physics
        /// </summary>
        TechSchoolPhysics,
        /// <summary>
        /// Tech school of Social, Arts and Science
        /// </summary>
        TechSchoolSocialArts
    }


    /// <summary>
    /// The technology school.
    /// </summary>
    public class TechSchool
    {
        /// <summary>
        /// List of all Technology Fields for the Technology School
        /// </summary>
        /// <remarks>
        /// This list will be setup in the <see cref="TechTables.MapTechFieldsToTechSchools" /> function
        /// </remarks>
        [XmlIgnore]
        internal List<TechField> techFields = new List<TechField>();

        /// <summary>
        /// Gets the list of the Technology fields for this school
        /// </summary>
        /// <value>The tech fields.</value>
        [XmlIgnore]
        public List<TechField> TechFields
        {
            get { return techFields; }
        }

        /// <remarks/>
        [XmlAttribute]
        public string ColumnHeadingsStart { get; set; }

        /// <summary>
        /// Gets the name of the technology school
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return StringManager.GetString(kUnicode); }
        }

        /// <remarks/>
        [XmlAttribute]
        public string kUnicode { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Type_ID { get; set; }


        /// <summary>
        /// Converts the Type-ID to a school type
        /// </summary>
        [XmlIgnore]
        public TechSchoolType SchoolType
        {
            get
            {
                switch (Type_ID)
                {
                    case "EB":
                        return TechSchoolType.TechSchoolEconomy;
                    case "EN":
                        return TechSchoolType.TechSchoolEnergy;
                    case "BS":
                        return TechSchoolType.TechSchoolBiology;
                    case "MC":
                        return TechSchoolType.TechSchoolMathematics;
                    case "PS":
                        return TechSchoolType.TechSchoolPhysics;
                    case "SS":
                        return TechSchoolType.TechSchoolSocialArts;
                    default:
                        Ensure.ThrowException(string.Format("Illegal technology school{0}", Type_ID));
                        break;
                }
                return TechSchoolType.TechSchoolUndefined;
            }
        }
    }

    /// <summary>
    /// A Technology field
    /// </summary>
    public class TechField
    {
        /// <summary>
        /// List of all Tech Applications for this Tech Field
        /// </summary>
        private readonly List<TechApplication> techApplications = new List<TechApplication>();

        /// <summary>
        /// List of all tech fields, which this tech field is depending from.
        /// </summary>
        private readonly List<TechFieldDependency> techFieldDependencies = new List<TechFieldDependency>();

        /// <summary>
        /// Gets the tech applications for this Tech Field
        /// </summary>
        /// <value>The tech applications.</value>
        public List<TechApplication> TechApplications
        {
            get { return techApplications; }
        }

        /// <remarks/>
        [XmlAttribute]
        public string ColumnHeadingsStart { get; set; }

        /// <summary>
        /// Gets the name of the technology field
        /// </summary>
        /// <value>The name.</value>
        [XmlIgnore]
        public string Name
        {
            get { return StringManager.GetString(kUnicode); }
        }


        /// <summary>
        /// Gets or sets the tech school.
        /// </summary>
        /// <value>The tech school.</value>
        [XmlIgnore]
        public TechSchool TechSchool { get; internal set; }

        /// <summary>
        /// Gets the description of the technology field
        /// </summary>
        /// <value>The description.</value>
        [XmlIgnore]
        public string Description
        {
            get { return StringManager.GetString(kUnicodD); }
        }

        /// <remarks/>
        [XmlAttribute]
        public string kUnicode { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string kUnicodD { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Type_ID { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string School_Catagories { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string IconName { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Field_Dependencies { get; set; }

        /// <summary>
        /// Gets or sets the research_ cost_ multiplier.
        /// </summary>
        /// <value>The research_ cost_ multiplier.</value>
        [XmlAttribute]
        public double Research_Cost_Multiplier { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public double Research_Cost_Exponent_Base { get; set; }

        /// <summary>
        /// Gets to the list of tech field dependencies.
        /// </summary>
        /// <value>The tech field dependencies.</value>
        public List<TechFieldDependency> TechFieldDependencies
        {
            get
            {
                return techFieldDependencies;
            }
        }

        /// <summary>
        /// Calculates the research points for the given level.
        /// </summary>
        /// <remarks>
        /// Formula:  
        /// research_cost_exponents ^techlevel * cost_multiplizer * tech level
        /// </remarks>
        /// <param name="techLevel">The tech level.</param>
        /// <returns></returns>
        public int CalculateResearchPointsForLevel(int techLevel)
        {
            Require.IsTrue(techLevel >= 0);
            return Convert.ToInt32(Math.Pow(Research_Cost_Exponent_Base, techLevel) * Research_Cost_Multiplier) * techLevel;
        }
    }

    /// <summary>
    /// Dependency between a TechApplication and a Tech Field
    /// </summary>
    public class TechFieldDependency
    {
        /// <summary>
        /// The dependend technology field
        /// </summary>
        public TechField TechField { get; set; }

        /// <summary>
        /// The minimum level of the dependend technology field
        /// </summary>
        public int TechLevel { get; set; }


        /// <summary>
        /// Initializes a new instance of the <see cref="TechFieldDependency"/> class.
        /// </summary>
        /// <remarks>
        /// This constructor will be needed for serializing this
        /// </remarks>
        public TechFieldDependency()
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TechFieldDependency"/> class.
        /// </summary>
        /// <param name="techId">The tech id.</param>
        /// <param name="techLevel">The tech level.</param>
        public TechFieldDependency(string techId, int techLevel)
        {
            TechField = SpreadSheetManager.TechTables.TechFieldDictionary[techId];
            TechLevel = techLevel;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TechFieldDependency"/> class.
        /// </summary>
        /// <param name="techField">The tech field.</param>
        /// <param name="techLevel">The tech level.</param>
        public TechFieldDependency(TechField techField, int techLevel)
        {
            TechField = techField;
            TechLevel = techLevel;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return string.Format("{0} Level {1}", TechField.Name, TechLevel);
        }
    }

    /// <summary>
    /// A concrete technology, which could be researched via applied research.
    /// </summary>
    public class TechApplication
    {
        /// <summary>
        /// List of the Technology Field Dependencies of the tech
        /// </summary>
        private readonly List<TechFieldDependency> techFieldDependencies = new List<TechFieldDependency>();

        /// <summary>
        /// Gets the tech field dependencies.
        /// </summary>
        /// <value>The tech field dependencies.</value>
        public List<TechFieldDependency> TechFieldDependencies
        {
            get
            {
                return techFieldDependencies;
            }
        }

        private TechFieldDependency defaultTechFieldDependency;

        /// <summary>
        /// Gets the default technology field.
        /// </summary>
        /// <value>The default tech field.</value>
        public TechFieldDependency DefaultTechFieldDependency
        {
            get
            {
                if (defaultTechFieldDependency == null)
                {

                    // todo: Ungültige Field Dependencies müssen schleunigst aus TechTables.xml eliminiert werden.
                    try
                    {
                        defaultTechFieldDependency = techFieldDependencies[0];
                    }
                    catch (Exception)
                    {
                        defaultTechFieldDependency = new TechFieldDependency(new TechField(), -99);
                    }

                }
                return defaultTechFieldDependency;
            }
        }

        /// <summary>
        /// Gets the name of the tech application
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get { return StringManager.GetString(kUnicode); }
        }

        /// <summary>
        /// Gets the description of the technlogy application
        /// </summary>
        /// <value>The description.</value>
        [XmlIgnore]
        public string Description
        {
            get { return StringManager.GetString(kUnicodD); }
        }

        /// <summary>
        /// Gets the description abbrev.
        /// </summary>
        /// <value>The description abbrev.</value>
        [XmlIgnore]
        public string DescriptionAbbrev
        {
            get
            {
                string description = Description;
                if (description.Length > 80)
                    description = description.Substring(0, 80) + "...";
                return description;
            }
        }



        /// <remarks/>
        [XmlAttribute]
        public string ColumnHeadingsStart { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string kUnicode { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string kUnicodD { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string kUnicdLg { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Sort_Tag { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Type_ID { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Application_Class { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Field_Dependencies { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string IconName { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Global_Tags { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Tag_Values { get; set; }

        /// <summary>
        /// A default technology resides on all fieldependencies on tech level 0 
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is default technology; otherwise, <c>false</c>.
        /// </value>
        public bool IsDefaultTechnology
        {
            get
            {
                foreach (var fieldDependency in techFieldDependencies)
                {
                    if (fieldDependency.TechLevel > 0) return false;
                }
                return true;
            }
        }
    }
}